#pragma once

template <bool IsConst>
_hBit<IsConst>& _hBit<IsConst>::operator=(bool b)
{
	assert(!IsConst);
	size_t pos = _owner._beg + _id;
	assert(pos < _owner._end);

	auto& chArr = _owner._data[pos >> dtBitOffset];
	auto& bit = ((_hBitStrDetail::Bit8*)&chArr)[(pos & dtBitMask) >> 3];
	bit[pos & 7] = b;
	return *this;
}

template <bool IsConst>
auto _hBitIter<IsConst>::operator->()
{
	assert(_pOwner);
	assert(_pOwner->_beg + _id < _pOwner->_end);
	return std::make_shared<_hBit<IsConst>>(*_pOwner, _id);
}

template <bool IsConst>
auto& _hBitIter<IsConst>::operator++()
{
	assert(_pOwner);
	assert(_pOwner->_beg + _id < _pOwner->_end);
	++_id;
	return *this;
}

template <bool IsConst>
auto _hBitIter<IsConst>::operator++(int)
{
	assert(_pOwner);
	assert(_pOwner->_beg + _id < _pOwner->_end);

	size_t oldId = _id++;
	return _hBitIter<IsConst>{_pOwner, oldId};
}

template <bool IsConst>
bool _hBitIter<IsConst>::operator==(const _hBitIter<IsConst>& other) const
{
	return _pOwner == other._pOwner && _id == other._id;
}

template <bool IsConst>
_hBit<IsConst>::operator bool() const
{
	size_t pos = _owner._beg + _id;
	assert(pos < _owner._end);

	auto& chArr = _owner._data[pos >> dtBitOffset];
	auto& bit = ((_hBitStrDetail::Bit8*)&chArr)[(pos & dtBitMask) >> 3];
	return bit[pos & 7];
}

template <size_t N>
uint64_t hBitStr::getUint() const
{
	if (_beg >= _end)
		return 0;

	size_t tmpSize = size();
	size_t tmpBeg = 0;
	if (tmpSize > N)
		tmpBeg = tmpSize - N;

	uint64_t tmp = 0;
	for (; tmpBeg < tmpSize; ++tmpBeg)
		tmp += (uint64_t)operator[](tmpBeg) << (tmpSize - tmpBeg - 1);

	return tmp;
}

template <size_t N>
hBitStr& hBitStr::operator<<(const std::bitset<N>& s)
{
	for (size_t i = 0; i < N; ++i)
		operator<<(s[N - i - 1]);

	return *this;
}

template <size_t N>
hBitStr& hBitStr::operator>>(std::bitset<N>& s)
{
	s.reset();
	for (size_t i = 0; _beg < _end && i < N; ++i, ++_beg)
	{
		while (dtBitSize <= _beg)
		{
			_beg -= dtBitSize;
			_end -= dtBitSize;
			_data.pop_front();
		}

		_hBitStrDetail::Bit8 bit;
		bit._val  = ((char*)&_data.front())[_beg >> 3];
		s.set(N - i - 1, bit[_beg & 7]);
	}

	return *this;
}

template <typename T>
T&  hBitStrView::popFront(size_t n, T& t)
{
	size_t tmpSize = _str.size();
	if (_id >= tmpSize)
		return t;

	size_t tmpVal = 0;
	n = std::min(tmpSize, n + _id);
	for (size_t i = _id; i < n; ++i)
	{
		if (_str[i])
			tmpVal += 1 << (n - i - 1);
	}

	_id = n;
	return t = T(tmpVal);
}

namespace _hBitStrDetail
{
	template <bool IsConst>
	Bit<IsConst>& Bit<IsConst>::operator=(bool b)
	{
		assert(!IsConst);
		assert(_id < 8);

		switch (_id)
		{
		case 0:
			if (b) _owner._uval |= 0x80;
			else _owner._uval &= 0x7f;
		case 1:
			if (b) _owner._uval |= 0x40;
			else _owner._uval &= 0xbf;
		case 2:
			if (b) _owner._uval |= 0x20;
			else _owner._uval &= 0xdf;
		case 3:
			if (b) _owner._uval |= 0x10;
			else _owner._uval &= 0xef;
		case 4:
			if (b) _owner._uval |= 0x8;
			else _owner._uval &= 0xf7;
		case 5:
			if (b) _owner._uval |= 0x4;
			else _owner._uval &= 0xfb;
		case 6:
			if (b) _owner._uval |= 0x2;
			else _owner._uval &= 0xfd;
		case 7:
			if (b) _owner._uval |= 0x1;
			else _owner._uval &= 0xfe;
		default:
			break;
		}

		return *this;
	}

	template <bool IsConst>
	Bit<IsConst>::operator bool() const
	{
		assert(_id < 8);
		switch (_id)
		{
		case 0:
			return _owner._uval & 0x80;
		case 1:
			return _owner._uval & 0x40;
		case 2:
			return _owner._uval & 0x20;
		case 3:
			return _owner._uval & 0x10;
		case 4:
			return _owner._uval & 0x8;
		case 5:
			return _owner._uval & 0x4;
		case 6:
			return _owner._uval & 0x2;
		case 7:
			return _owner._uval & 0x1;
		default:
			break;
		}

		return false;
	}
}